<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta
      name="viewport"
      content="initial-scale=1.0, user-scalable=no, width=device-width"
    />
    <title>轨迹回放与录制（WebM → MP4）</title>
    <link
      rel="stylesheet"
      href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css"
    />
    <style>
      html,
      body,
      #container {
        height: 100%;
        width: 100%;
      }

      .input-card .btn {
        margin-right: 1.2rem;
        width: 9rem;
      }

      .input-card .btn:last-child {
        margin-right: 0;
      }
    </style>
  </head>
  <body>
    <div id="container"></div>
    <div class="input-card">
      <h4>轨迹控制</h4>
      <div class="input-item">
        <input
          type="button"
          class="btn"
          value="开始动画"
          onclick="startAnimation()"
        />
        <input
          type="button"
          class="btn"
          value="暂停动画"
          onclick="pauseAnimation()"
        />
      </div>
      <div class="input-item">
        <input
          type="button"
          class="btn"
          value="继续动画"
          onclick="resumeAnimation()"
        />
        <input
          type="button"
          class="btn"
          value="停止动画"
          onclick="stopAnimation()"
        />
      </div>
    </div>

    <!-- 1. 引入 FFmpeg.wasm -->
    <script src="https://cdn.bootcdn.net/ajax/libs/ffmpeg/0.11.6/ffmpeg.min.js"></script>
    <script
      type="text/javascript"
      src="https://webapi.amap.com/maps?v=2.0&key=03437a6ddeb44bd6ef16e80b1bf10bcf&plugin=AMap.PlaceSearch,AMap.Scale,AMap.OverView,AMap.ToolBar,AMap.MapType,AMap.Geolocation,AMap.Geocoder,AMap.MoveAnimation,AMap.Driving,AMap.AutoComplete,AMap.MoveAnimation,AMap.TileLayer.Traffic,AMap.ToolBar"
    ></script>

    <script>
      const { createFFmpeg, fetchFile } = FFmpeg
      const ffmpeg = createFFmpeg({ log: true })
      let mediaRecorder = null
      let recordedChunks = []
      let isRecording = false
      let animationInProgress = false
      let timer = null

      async function startRecording() {
        try {
          const stream = await navigator.mediaDevices.getDisplayMedia({
            video: { mediaSource: 'screen' },
            audio: false,
            selfBrowserSurface: 'include',
            preferCurrentTab: true,
          })

          mediaRecorder = new MediaRecorder(stream, {
            // 这里仍然用 webm 录制
            mimeType: 'video/webm;codecs=h264',
            bitsPerSecond: 5000000,
          })

          recordedChunks = []
          mediaRecorder.ondataavailable = (e) => {
            if (e.data.size > 0) recordedChunks.push(e.data)
          }

          mediaRecorder.onstop = async () => {
            // 2. 拼接成 WebM Blob
            const webmBlob = new Blob(recordedChunks, { type: 'video/webm' })

            // 3. 确保 FFmpeg 加载好
            if (!ffmpeg.isLoaded()) {
              await ffmpeg.load()
            }

            // 4. 写入虚拟文件系统
            ffmpeg.FS('writeFile', 'input.webm', await fetchFile(webmBlob))

            // 5. 调用 FFmpeg 转码
            //    -c:v copy 如果只是容器转换，不重编码也可；也可指定编码参数
            await ffmpeg.run(
              '-i',
              'input.webm',
              '-c:v',
              'copy',
              '-c:a',
              'aac',
              'output.mp4'
            )

            // 6. 读取转码后的文件
            const data = ffmpeg.FS('readFile', 'output.mp4')
            const mp4Blob = new Blob([data.buffer], { type: 'video/mp4' })

            // 7. 生成下载链接并点击下载
            const url = URL.createObjectURL(mp4Blob)
            const a = document.createElement('a')
            a.href = url
            a.download = 'recording.mp4'
            document.body.appendChild(a)
            a.click()
            document.body.removeChild(a)
            URL.revokeObjectURL(url)

            // 清理 FFmpeg FS
            ffmpeg.FS('unlink', 'input.webm')
            ffmpeg.FS('unlink', 'output.mp4')
          }

          mediaRecorder.start()
          isRecording = true
          stream.getVideoTracks()[0].onended = () => stopRecording()
        } catch (err) {
          console.error('Error starting recording:', err)
          alert('屏幕录制已取消或发生错误')
        }
      }

      function stopRecording() {
        if (mediaRecorder && isRecording) {
          mediaRecorder.stop()
          isRecording = false
          if (mediaRecorder.stream) {
            mediaRecorder.stream.getTracks().forEach((t) => t.stop())
          }
        }
      }

      AMap.plugin('AMap.MoveAnimation', function () {
        var infoWindow = new AMap.InfoWindow({
          offset: new AMap.Pixel(0, -40),
          closeButton: false,
        })
        var marker, timer
        var lineArr = [
          [116.478935, 39.997761],
          [116.478939, 39.997825],
          [116.478912, 39.998549],
          [116.478912, 39.998549],
          [116.478998, 39.998555],
          [116.478998, 39.998555],
          [116.479282, 39.99856],
          [116.479658, 39.998528],
          [116.480151, 39.998453],
          [116.480784, 39.998302],
          [116.480784, 39.998302],
          [116.481149, 39.998184],
          [116.481573, 39.997997],
          [116.481863, 39.997846],
          [116.482072, 39.997718],
          [116.482362, 39.997718],
          [116.483633, 39.998935],
          [116.48367, 39.998968],
          [116.484648, 39.999861],
        ]
        var map = new AMap.Map('container', {
          resizeEnable: true,
          center: [116.397428, 39.90923],
          zoom: 20,
        })

        var satelliteLayer = new AMap.TileLayer.Satellite({
          zIndex: 12,
          autoRefresh: true,
          opacity: 1,
          map,
        })

        marker = new AMap.Marker({
          map,
          position: lineArr[0],
          icon: 'https://a.amap.com/jsapi_demos/static/demo-center-v2/car.png',
          offset: new AMap.Pixel(-13, -26),
        })
        infoWindow.open(map, marker.getPosition())

        var polyline = new AMap.Polyline({
          map,
          path: lineArr,
          showDir: true,
          strokeColor: '#28F',
          strokeWeight: 6,
        })
        var passedPolyline = new AMap.Polyline({
          map,
          strokeColor: '#AF5',
          strokeWeight: 6,
        })

        marker.on('moving', function (e) {
          clearTimeout(timer)
          passedPolyline.setPath(e.passedPath)
          map.setCenter(e.target.getPosition(), true)

          var pos = e.target.getPosition()
          infoWindow.setContent(
            `经度: ${pos.lng.toFixed(6)}<br>纬度: ${pos.lat.toFixed(6)}`
          )
          infoWindow.open(map, pos)

          // 2秒无新动画帧自动停止录制
          timer = setTimeout(() => stopRecording(), 2000)
        })

        map.setFitView()

        window.startAnimation = async function () {
          if (animationInProgress) return
          if (!confirm('需要授权屏幕录制，请点击允许')) return

          await startRecording()
          document.querySelector('.input-card').style.display = 'none'
          animationInProgress = true
          marker.moveAlong(
            lineArr,
            { duration: 500, autoRotation: true },
            () => {
              animationInProgress = false
            }
          )
        }

        window.pauseAnimation = () => marker.pauseMove()
        window.resumeAnimation = () => marker.resumeMove()
        window.stopAnimation = () => {
          stopRecording()
          document.querySelector('.input-card').style.display = ''
          marker.stopMove()
          infoWindow.close()
          animationInProgress = false
        }
      })
    </script>
  </body>
</html>
